package io.github.openfeign.spring.enhancer;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;

/**
 * 多部分表单流式处理字节码转换器
 *
 * <h3>核心改造：</h3>
 * <ul>
 *   <li>将表单处理从内存缓冲模式改造为延迟写入模式</li>
 *   <li>实现以下关键修改：
 *     <ol>
 *       <li>重构process方法，推迟实际写入操作</li>
 *       <li>新增延迟输出消费者模式实现</li>
 *       <li>优化头信息生成逻辑</li>
 *     </ol>
 *   </li>
 * </ul>
 *
 * <h3>代码改造对比：</h3>
 *
 * <h4>原始实现（内存缓冲模式）:</h4>
 * <pre>{@code
 * public void process(...) {
 *     // 即时生成完整字节数组
 *     val bytes = output.toByteArray();
 *     template.body(Request.Body.encoded(bytes, null));
 * }
 * }</pre>
 *
 * <h4>优化后实现（延迟流式模式）:</h4>
 * <pre>{@code
 * public void process(...) {
 *     // 设置延迟消费者
 *     template.setDelayOutput(new DelayOutputConsumer(...));
 * }
 * }</pre>
 *
 * <h3>新增组件说明：</h3>
 * <div class="class-description">
 *   <h4>DelayOutputConsumer</h4>
 *   <p>延迟输出消费者，实现以下核心功能：</p>
 *   <ul>
 *     <li><strong>按需写入机制：</strong>在输出流可用时执行实际写入操作</li>
 *     <li><strong>上下文保持：</strong>封装处理过程所需的所有上下文参数</li>
 *     <li><strong>流式生命周期管理：</strong>统一管理输出流的打开/关闭</li>
 *   </ul>
 *   <pre>{@code
 *   public void accept(Object stream) {
 *       Output output = new Output(charset, (OutputStream) stream);
 *       // 实际写入操作延迟至此执行
 *   }
 *   }</pre>
 * </div>
 *
 * @see feign.RequestTemplate 请求模板类
 * @see java.util.function.Consumer 消费者接口
 */
@Slf4j
public class MultipartFormContentProcessorBytecodeTransformer extends AbstractBytecodeTransformer {

    public MultipartFormContentProcessorBytecodeTransformer(final ClassPool classPool, final String classOutPath) {
        super(classPool, classOutPath);
    }

    /**
     * {@inheritDoc}
     */
    public void transformBytecode() throws NotFoundException, CannotCompileException {
        if(log.isDebugEnabled()){
            log.debug("Start transforming feign.form.MultipartFormContentProcessor");
        }
        final CtClass ctClass = classPool.get("feign.form.MultipartFormContentProcessor");

        // 创建静态内部类 DelayOutputConsumer
        createDelayOutputConsumerClass(ctClass);

        // 找出需要修改的 process
        final CtMethod method = ctClass.getDeclaredMethod(
                "process",
                new CtClass[]{classPool.get("feign.RequestTemplate"), classPool.get("java.nio.charset.Charset"), classPool.get("java.util.Map")}
        );

        // 修改 process 方法
        method.setBody("{\n" +
                "   String boundary = Long.toHexString(System.currentTimeMillis());\n" +
                "   String contentTypeHeaderValue = new StringBuilder()\n" +
                "                .append($0.getSupportedContentType().getHeader())\n" +
                "                .append(\"; charset=\").append($2.name())\n" +
                "                .append(\"; boundary=\").append(boundary)\n" +
                "                .toString();\n" +
                "   $1.header(CONTENT_TYPE_HEADER, (java.lang.Iterable)null); \n" +
                "   $1.header(CONTENT_TYPE_HEADER,  new String[]{contentTypeHeaderValue});\n" +
                "   $1.setDelayOutput(new feign.form.MultipartFormContentProcessor.DelayOutputConsumer(boundary, $3, $1, $2, $0));\n" +
                "}\n"
        );
        // 应用修改
        redefineClassInMemory(ctClass);
        if(log.isDebugEnabled()){
            log.debug("Successfully transforming feign.form.MultipartFormContentProcessor");
        }
    }

    private void createDelayOutputConsumerClass(final CtClass ctClass) throws NotFoundException, CannotCompileException {
        final CtClass consumerClass = classPool.get("java.util.function.Consumer");

        // 创建静态内部类 OutConsumer
        final CtClass delayOutputConsumerClass = ctClass.makeNestedClass("DelayOutputConsumer", true); // true 表示是静态内部类
        delayOutputConsumerClass.setInterfaces(new CtClass[]{consumerClass});

        // 添加字段
        delayOutputConsumerClass.addField(CtField.make("private String boundary;", delayOutputConsumerClass));
        delayOutputConsumerClass.addField(CtField.make("private java.util.Map data;", delayOutputConsumerClass));
        delayOutputConsumerClass.addField(CtField.make("private feign.RequestTemplate template;", delayOutputConsumerClass));
        delayOutputConsumerClass.addField(CtField.make("private java.nio.charset.Charset charset;", delayOutputConsumerClass));
        delayOutputConsumerClass.addField(CtField.make("private feign.form.MultipartFormContentProcessor processor;", delayOutputConsumerClass));

        // 添加构造方法
        final CtConstructor constructor = CtNewConstructor.make(
                "public DelayOutputConsumer(String boundary, java.util.Map data, " +
                        "    feign.RequestTemplate template, java.nio.charset.Charset charset, feign.form.MultipartFormContentProcessor processor) { \n" +
                        "    this.boundary = boundary;\n" +
                        "    this.data = data;\n" +
                        "    this.template = template;\n" +
                        "    this.charset = charset;\n" +
                        "    this.processor = processor;\n" +
                        "}\n",
                delayOutputConsumerClass
        );
        delayOutputConsumerClass.addConstructor(constructor);

        // 实现 accept ， 延迟写入数据到 outputStream
        delayOutputConsumerClass.addMethod(
                CtNewMethod.make("public void accept(Object o) {\n" +
                                "        feign.form.multipart.Output output = new feign.form.multipart.Output($0.charset,(java.io.OutputStream)$1);\n" +
                                "        java.util.Iterator iter = $0.data.entrySet().iterator();\n" +
                                "        while (iter.hasNext()) {\n" +
                                "           java.util.Map.Entry entry = (java.util.Map.Entry) iter.next();\n" +
                                "           if (entry.getKey() == null || entry.getValue() == null) continue;\n" +
                                "           feign.form.multipart.Writer writer = $0.processor.findApplicableWriter(entry.getValue());\n" +
                                "           writer.write(output, $0.boundary, (String)entry.getKey(), entry.getValue());\n" +
                                "        } " +
                                "        output.write(\"--\").write($0.boundary).write(\"--\").write(feign.form.ContentProcessor.CRLF);\n" +
                                "}\n",
                        delayOutputConsumerClass)
        );

        redefineClassInMemory(delayOutputConsumerClass);
    }


}
